package com.yn.pet.web.service.biz;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yn.pet.bus.dao.DeviceInfoDao;
import com.yn.pet.bus.entity.*;

import com.yn.pet.bus.enums.DeviceStatusEnum;
import com.yn.pet.bus.enums.FenceTypeEnum;
import com.yn.pet.bus.enums.LocationTypeEnum;
import com.yn.pet.bus.model.BaseStation;
import com.yn.pet.bus.model.WifiInfo;
import com.yn.pet.protocol.commons.transform.AttributeKey;
import com.yn.pet.protocol.t808.T0200;
import com.yn.pet.web.endpoint.JT808Endpoint;
import com.yn.pet.web.service.*;
import com.yn.pet.web.util.GPSConverterUtils;
import com.yn.pet.web.util.LocationUtil;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.geo.Point;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class LocationRealtimeBizService {
    private static final Logger log = LoggerFactory.getLogger(LocationRealtimeBizService.class);

    private final DeviceSimService deviceSimService;
    private final LocationRealtimeService locationRealtimeService;
    private final DeviceInfoService deviceInfoService;
    private final GenFenceService genFenceService;
    private final GenFenceAlarmService genFenceAlarmService;
    private static final double EARTH_RADIUS = 6371.0; // 地球半径，单位为公里
    private final DeviceSyncSettingsService deviceSyncSettingsService;
    private final AlarmRecordService alarmRecordService;

    @Transactional
    public void saveLocationRealtime(List<T0200> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        Set<String> imeiSet = list.stream().map(T0200::getClientId).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(imeiSet)) {
            return;
        }

        List<LocationRealtime> locationRealtimeList = this.getLocationRealtimeList(list);
        if (CollectionUtils.isEmpty(locationRealtimeList)) {
            return;
        }

        locationRealtimeService.saveBatch(locationRealtimeList);
        //运动的设备
        Set<Long> movingDeviceIdSet = locationRealtimeList.stream().filter(locationRealtime -> Objects.equals(locationRealtime.getDeviceStatus(), DeviceStatusEnum.MOVING.getCode())).map(LocationRealtime::getDeviceId).collect(Collectors.toSet());
        //静止的设备
        Set<Long> staticDeviceIdSet = locationRealtimeList.stream().filter(locationRealtime -> Objects.equals(locationRealtime.getDeviceStatus(), DeviceStatusEnum.STATIC.getCode())).map(LocationRealtime::getDeviceId).collect(Collectors.toSet());
        if (!CollectionUtils.isEmpty(staticDeviceIdSet)) {
            //本次 静止的设备中 上次不是静止状态。改成静止的
            List<DeviceInfo> noStaticDeviceInfoList = deviceInfoService.listByIds(staticDeviceIdSet).stream().filter(deviceInfo -> !Objects.equals(deviceInfo.getDeviceStatus(), DeviceStatusEnum.STATIC.getCode())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(noStaticDeviceInfoList)){
                List<DeviceInfo> deviceInfoList = noStaticDeviceInfoList.stream().peek(deviceInfo -> {
                    deviceInfo.setDeviceStatus(DeviceStatusEnum.STATIC.getCode());
                    deviceInfo.setStatusChangeTime(new Date());
                }).collect(Collectors.toList());
                deviceInfoService.updateBatchById(deviceInfoList);
            }

        }
        if (!CollectionUtils.isEmpty(movingDeviceIdSet)) {
            List<DeviceInfo> noMovingDeviceInfoList = deviceInfoService.listByIds(movingDeviceIdSet).stream().filter(deviceInfo -> !Objects.equals(deviceInfo.getDeviceStatus(), DeviceStatusEnum.MOVING.getCode())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(noMovingDeviceInfoList)){
                List<DeviceInfo> deviceInfoList = noMovingDeviceInfoList.stream().peek(deviceInfo -> {
                    deviceInfo.setDeviceStatus(DeviceStatusEnum.MOVING.getCode());
                    deviceInfo.setStatusChangeTime(new Date());
                }).collect(Collectors.toList());
                deviceInfoService.updateBatchById(deviceInfoList);
            }

        }


        log.info("保存位置数据:{}", locationRealtimeList);
        Set<Long> deviceIdSet = locationRealtimeList.stream().map(LocationRealtime::getDeviceId).collect(Collectors.toSet());
        log.info("deviceIdSet:{}", deviceIdSet);
        DeviceSyncSettings deviceSyncSettings = deviceSyncSettingsService.list(new LambdaQueryWrapper<DeviceSyncSettings>().in(DeviceSyncSettings::getDeviceId, deviceIdSet)).stream().findAny().orElse(null);
        log.info("deviceSyncSettings:{}", deviceSyncSettings);
        if (deviceSyncSettings != null && deviceSyncSettings.getFenceEnabled() == 1) {
            List<GenFenceAlarm> genFenceAlarmList = this.getGenFenceAlarmList(locationRealtimeList);
            log.info("保存围栏数据:{}", genFenceAlarmList);
            if (!CollectionUtils.isEmpty(genFenceAlarmList)) {
                genFenceAlarmService.saveBatch(genFenceAlarmList);
            }
        }

        if (deviceSyncSettings != null && deviceSyncSettings.getLowBatteryEnabled() == 1) {
            List<AlarmRecord> alarmRecordList = locationRealtimeList.stream().filter(locationRealtime -> Objects.equals(locationRealtime.getBatteryAlarm(), 1) || locationRealtime.getBattery() <= 20).map(locationRealtime -> {
                AlarmRecord alarmRecord = new AlarmRecord();
                alarmRecord.setLocationId(locationRealtime.getLocationId());
                alarmRecord.setDeviceId(locationRealtime.getDeviceId());
                alarmRecord.setType(2);
                alarmRecord.setCreateTime(new Date());
                alarmRecord.setUpdateTime(new Date());
                return alarmRecord;
            }).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(alarmRecordList)) {
                alarmRecordService.saveBatch(alarmRecordList);
            }
        }

    }

    /**
     * 获取进出围栏数据
     *
     * @param locationRealtimeList 位置数据
     * @return 进出围栏数据
     */
    private List<GenFenceAlarm> getGenFenceAlarmList(List<LocationRealtime> locationRealtimeList) {
        List<GenFenceAlarm> genFenceAlarms = new ArrayList<>();
        Set<Long> deviceIds = locationRealtimeList.stream().map(LocationRealtime::getDeviceId).collect(Collectors.toSet());
        List<GenFence> genFenceList = genFenceService.list(new LambdaQueryWrapper<GenFence>().in(GenFence::getDeviceId, deviceIds).eq(GenFence::getUseStatus, 1).eq(GenFence::getIsDelete, 0));
        if (CollectionUtils.isEmpty(genFenceList)) {
            return genFenceAlarms;
        }
        for (GenFence genFence : genFenceList) {
            for (LocationRealtime locationRealtime : locationRealtimeList) {
                GenFenceAlarm genFenceAlarm = new GenFenceAlarm();
                Integer fenceType = genFence.getFenceType();
                FenceTypeEnum fenceTypeEnum = FenceTypeEnum.getByCode(fenceType);
                if (fenceTypeEnum == FenceTypeEnum.CIRCLE) {
                    Integer radius = genFence.getRadius();
                    Double centerLat = genFence.getCenterLat();
                    Double centerLng = genFence.getCenterLng();
                    boolean inCircle = this.isPointInCircle(centerLat, centerLng, radius, locationRealtime.getLatitude(), locationRealtime.getLongitude());
                    if (inCircle) {
                        genFenceAlarm.setAlarmType(1);
                    } else {
                        genFenceAlarm.setAlarmType(2);
                    }

                } else if (fenceTypeEnum == FenceTypeEnum.POLYGON) {
                    String vertices = genFence.getVertices();
                    List<Point> pointList = JSON.parseArray(vertices, Point.class);
                    double[] polygon = new double[pointList.size() * 2];
                    for (int i = 0; i < pointList.size(); i++) {
                        polygon[i * 2] = pointList.get(i).getX();
                        polygon[i * 2 + 1] = pointList.get(i).getY();
                    }
                    boolean pointInPolygon = this.isPointInPolygon(locationRealtime.getLongitude(), locationRealtime.getLatitude(), polygon);
                    if (pointInPolygon) {
                        genFenceAlarm.setAlarmType(1);
                    } else {
                        genFenceAlarm.setAlarmType(2);
                    }
                }
                genFenceAlarm.setFenceId(genFence.getFenceId());
                genFenceAlarm.setDeviceId(genFence.getDeviceId());
                genFenceAlarm.setAlarmTime(new Date());
                genFenceAlarm.setLocationId(locationRealtime.getLocationId());


                LambdaQueryWrapper<GenFenceAlarm> lambdaQuery = Wrappers.lambdaQuery();
                GenFenceAlarm existGenFenceAlarm = genFenceAlarmService.list(lambdaQuery.eq(GenFenceAlarm::getFenceId, genFence.getFenceId())
                        .orderByDesc(GenFenceAlarm::getCreateTime)
                        .last("limit 1")).stream().findAny().orElse(null);
                if (existGenFenceAlarm == null || !Objects.equals(existGenFenceAlarm.getAlarmType(), genFenceAlarm.getAlarmType())) {
                    genFenceAlarms.add(genFenceAlarm);
                }

            }
        }
        return genFenceAlarms;
    }

    /**
     * 获取位置数据
     *
     * @param list 终端位置数据
     * @return 位置数据
     */
    private List<LocationRealtime> getLocationRealtimeList(List<T0200> list) {
        Set<String> imeiSet = list.stream().map(T0200::getClientId).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(imeiSet)) {
            return null;
        }
        Map<String, Long> deviceIdMap = deviceInfoService.list(new LambdaQueryWrapper<DeviceInfo>().in(DeviceInfo::getImei, imeiSet)).stream().collect(Collectors.toMap(DeviceInfo::getImei, DeviceInfo::getDeviceId, (v1, v2) -> v1));

        return list.stream().filter(t -> deviceIdMap.containsKey(t.getClientId())).map(t -> {
            Map<Integer, Object> attributes = t.getAttributes();
            attributes.forEach((key, value) -> {
                log.info("key:{},value:{}", key, value);
            });

            LocationRealtime locationRealtime = new LocationRealtime();
            locationRealtime.setDeviceId(deviceIdMap.get(t.getClientId()));

            int warnBit = t.getWarnBit();
            String warnBitStr = String.format("%32s", Integer.toBinaryString(warnBit)).replace(' ', '0');
            log.info("warnBitStr------------>{}", warnBitStr);
            String batteryAlarm = warnBitStr.substring(warnBitStr.length() - 8, warnBitStr.length() - 7);
            locationRealtime.setBatteryAlarm(Integer.parseInt(batteryAlarm));

//            int statusBit = t.getStatusBit();
//            String statusBitStr = String.format("%32s", Integer.toBinaryString(statusBit)).replace(' ', '0');
//            String status = statusBitStr.substring(10, 11);

            byte[] status =(byte[]) t.getAttributes().get(AttributeKey.status);
            log.info("status------------>{}", status);
            if (status!=null && status[0] == 1) {
                locationRealtime.setDeviceStatus(DeviceStatusEnum.MOVING.getCode());
            } else {
                locationRealtime.setDeviceStatus(DeviceStatusEnum.STATIC.getCode());
            }


            int gnssCount = Integer.parseInt(t.getAttributes().get(AttributeKey.GnssCount).toString());
            log.info("定位卫星数量------------>{}", gnssCount);


            byte[] battery = (byte[]) t.getAttributes().get(AttributeKey.Battery);
            if (gnssCount > 0) {
                log.info("定位方式-------------->{}", LocationTypeEnum.GPS.getCode());
                String lng_lat = GPSConverterUtils.gps84_To_Gcj02(t.getLng(), t.getLat());
                log.info("经纬度-------------->{}", lng_lat);
                String[] lng_lat_array = lng_lat.split(",");
                locationRealtime.setLocationType(LocationTypeEnum.GPS.getCode());
                locationRealtime.setLatitude(Double.parseDouble(lng_lat_array[1]));
                locationRealtime.setLongitude(Double.parseDouble(lng_lat_array[0]));
            } else {
                List<BaseStation> baseStationList = new ArrayList<>();
                List<WifiInfo> wifiInfoList = new ArrayList<>();
                byte[] baseStation = (byte[]) t.getAttributes().get(AttributeKey.BaseStation);
                log.info("基站信息-------------->{}", baseStation);
                byte[] wifiInfo = (byte[]) t.getAttributes().get(AttributeKey.WifiInfo);
                log.info("wifi信息-------------->{}", wifiInfo);
                if (wifiInfo == null || wifiInfo.length == 0) {
                    locationRealtime.setLocationType(LocationTypeEnum.CELL.getCode());
                    log.info("定位方式----------->{}", LocationTypeEnum.CELL.getCode());
                    String baseStationStr = bytesToHex(baseStation);
                    log.info("基站信息-------------->{}", baseStationStr);
                    //01CC 0000 00 B8 95 04 D7 87 C6 00
                    int mcc = Integer.parseInt(baseStationStr.substring(0, 4), 16);
                    int mnc = Integer.parseInt(baseStationStr.substring(4, 8), 16);
                    String baseStationInfo = baseStationStr.substring(8);
                    //00B895 04D787C6 00
                    do {
                        String lacStr = baseStationInfo.substring(0, 6);
                        int lac = Integer.parseInt(lacStr, 16);
                        String cellidStr = baseStationInfo.substring(6, 14);
                        int cellid = Integer.parseInt(cellidStr, 16);
                        String rssStr = baseStationInfo.substring(14, 16);
                        int rss = Integer.parseInt(rssStr, 16);
                        baseStationList.add(new BaseStation(mcc, mnc, lac, cellid, rss));
                        baseStationInfo = baseStationInfo.substring(16);
                    } while (!StringUtils.isBlank(baseStationInfo));

                } else {
                    log.info("定位方式----------->{}", LocationTypeEnum.WIFI.getCode());
                    locationRealtime.setLocationType(LocationTypeEnum.WIFI.getCode());
                    String wifiInfoStr = bytesToHex(wifiInfo);
                    log.info("wifi信息-------------->{}", wifiInfoStr);
                    do {
                        String mac = wifiInfoStr.substring(0, 12);
                        String rssi = wifiInfoStr.substring(12, 14);
                        wifiInfoList.add(new WifiInfo(mac, Integer.parseInt(rssi, 16)));
                        wifiInfoStr = wifiInfoStr.substring(14);
                    } while (!StringUtils.isBlank(wifiInfoStr));
                }
                Tuple2<Double, Double> latitude_longitude = LocationUtil.getLocationByTX(t.getClientId(), baseStationList, wifiInfoList);
                if (latitude_longitude != null) {
                    locationRealtime.setLatitude(latitude_longitude.getT1());
                    locationRealtime.setLongitude(latitude_longitude.getT2());
                }
            }

            locationRealtime.setAltitude((double) t.getAltitude());
            locationRealtime.setSpeed(t.getSpeed());
            locationRealtime.setDirection(t.getDirection());
            locationRealtime.setSatelliteNum((int) t.getAttributes().get(AttributeKey.GnssCount));
            locationRealtime.setBattery((int) battery[1]);
            locationRealtime.setSignalStrength((int) t.getAttributes().get(AttributeKey.SignalStrength));
            Date date = Date.from(t.getDeviceTime().atZone(ZoneId.systemDefault()).toInstant());
            locationRealtime.setGpsTime(date);


            String s = GPSConverterUtils.gcj_To_Gps84(locationRealtime.getLongitude(), locationRealtime.getLatitude());
            log.info("经纬度转换-------------->{}", s);
            String location = LocationUtil.getLocation(s.split(",")[1], s.split(",")[0]);
            locationRealtime.setDetailedLocation(location);
            locationRealtime.setReceiveTime(new Date());


            log.info("设备上报");
            return locationRealtime;
        }).collect(Collectors.toList());
    }

    /**
     * 判断点是否在圆形区域内
     *
     * @param centerLat 圆形中心点的纬度
     * @param centerLng 圆形中心点的经度
     * @param radius    圆形的半径
     * @param pointLat  要判断的点的纬度
     * @param pointLng  要判断的点的经度
     * @return
     */
    private boolean isPointInCircle(double centerLat, double centerLng, double radius, double pointLat, double pointLng) {
        double distance = this.getDistance(centerLat, centerLng, pointLat, pointLng);
        return distance <= radius;
    }

    /**
     * 判断点是否在多边形区域内
     *
     * @param pointLon 点的经度
     * @param pointLat 点的纬度
     * @param polygon  多边形的经纬度数组，格式为：[lon1, lat1, lon2, lat2, ..., lonN, latN]
     * @return true 表示点在多边形内，false 表示在多边形外
     */
    private boolean isPointInPolygon(double pointLon, double pointLat, double[] polygon) {
        int n = polygon.length / 2;
        boolean isInside = false;

        for (int i = 0, j = n - 1; i < n; j = i++) {
            double xi = polygon[i * 2];
            double yi = polygon[i * 2 + 1];
            double xj = polygon[j * 2];
            double yj = polygon[j * 2 + 1];

            // 射线与边相交的判断
            boolean intersect = ((yi > pointLat) != (yj > pointLat)) &&
                    (pointLon < (xj - xi) * (pointLat - yi) / (yj - yi) + xi);

            if (intersect) {
                isInside = !isInside;
            }
        }

        return isInside;
    }

    /**
     * 计算两个经纬度之间的距离
     *
     * @param lat1 第一个经纬度
     * @param lon1 第一个经纬度
     * @param lat2 第二个经纬度
     * @param lon2 第二个经纬度
     * @return 距离，单位为米
     */
    private double getDistance(double lat1, double lon1, double lat2, double lon2) {
        // 将角度转换为弧度
        lat1 = Math.toRadians(lat1);
        lon1 = Math.toRadians(lon1);
        lat2 = Math.toRadians(lat2);
        lon2 = Math.toRadians(lon2);

        // 计算纬度差和经度差
        double dLat = lat2 - lat1;
        double dLon = lon2 - lon1;

        // 应用Haversine公式
        double a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dLon / 2), 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        // 距离，单位为m
        return EARTH_RADIUS * c * 1000; // 返回结果
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int b : bytes) {
            sb.append(String.format("%02X", b & 0xFF));
        }
        return sb.toString().trim();
    }
}
